// NOTE: This code requires <netinet/ip.h> and <netinet/tcp.h>
// It might also need adjustments based on specific Linux distribution header locations/content.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>  // sockaddr_in
#include <netinet/ip.h>  // struct ip (or iphdr depending on system)
#include <netinet/tcp.h> // struct tcphdr
#include <arpa/inet.h>   // inet_ntoa, ntohs
#include <net/if.h>      // struct ifreq, IFF_PROMISC
#include <sys/ioctl.h>   // ioctl, SIOCGIFFLAGS, SIOCSIFFLAGS
#include <unistd.h>      // close()

// Function Prototypes
int Open_Raw_Socket(void);
int Set_Promisc(const char *interface, int sock); // Use const char*

int main()
{
    int sock;
    sock = Open_Raw_Socket();
    if (sock < 0)
    {
        exit(1); // Error message printed in Open_Raw_Socket
    }
    printf("raw socket created: %d\n", sock);

    char buffer[65535]; // Max IP packet size
    ssize_t bytes_recieved;
    socklen_t fromlen;
    struct sockaddr_in from; // Address structure for recvfrom

    struct iphdr *ip_header;
    struct tcphdr *tcp_header;

    // Set network interface (e.g., "ens33", "enp0s3") to promiscuous mode
    const char *interface_name = "ens33"; // <<< CHANGE THIS IF NEEDED
    if (Set_Promisc(interface_name, sock) != 0)
    {
        close(sock);
        exit(1); // Error message printed in Set_Promisc
    }
    printf("Interface %s set to promiscuous mode.\n", interface_name);

    printf("IP header size (expected): %lu bytes\n", sizeof(struct iphdr));
    printf("TCP header size (minimum): %lu bytes\n", sizeof(struct tcphdr));

    while (1)
    {
        fromlen = sizeof(from);
        memset(buffer, 0, sizeof(buffer)); // Clear buffer

        bytes_recieved = recvfrom(sock, buffer, sizeof(buffer), 0,
                                  (struct sockaddr *)&from, &fromlen);

        if (bytes_recieved < 0)
        {
            if (errno == EINTR)
                continue; // Interrupted by signal
            perror("recvfrom error");
            continue; // Continue listening
        }

        if (bytes_recieved < sizeof(struct iphdr))
        {
            continue;
        }

        ip_header = (struct iphdr *)buffer;

        if (ip_header->version != 4)
        {
            continue;
        }

        if (ip_header->protocol != IPPROTO_TCP)
        {
            continue;
        }

        unsigned int ip_header_len = ip_header->ihl * 4;

        if (bytes_recieved < ip_header_len + sizeof(struct tcphdr))
        {
            continue;
        }

        tcp_header = (struct tcphdr *)(buffer + ip_header_len);

        printf("\n--- TCP Packet Received (%ld bytes) ---\n", bytes_recieved);

        char src_ip_str[INET_ADDRSTRLEN];
        char dst_ip_str[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &(ip_header->saddr), src_ip_str, INET_ADDRSTRLEN);
        inet_ntop(AF_INET, &(ip_header->daddr), dst_ip_str, INET_ADDRSTRLEN);

        printf("IP Header:\n");
        printf("  From: %s\n", src_ip_str);
        printf("  To:   %s\n", dst_ip_str);
        printf("  Header Length: %u bytes\n", ip_header_len);
        printf("  Total Length:  %u bytes\n", ntohs(ip_header->tot_len));
        printf("  Protocol: %u (TCP)\n", ip_header->protocol);
        printf("  TTL: %u\n", ip_header->ttl);
        printf("  Checksum: 0x%x\n", ntohs(ip_header->check));

        printf("TCP Header:\n");
        printf("  Source Port: %u\n", ntohs(tcp_header->source));
        printf("  Dest Port:   %u\n", ntohs(tcp_header->dest));
        printf("  Sequence Num: %u\n", ntohl(tcp_header->seq));
        printf("  Ack Num:      %u\n", ntohl(tcp_header->ack_seq));
        printf("  Flags: ");
        if (tcp_header->fin)
            printf("FIN ");
        if (tcp_header->syn)
            printf("SYN ");
        if (tcp_header->rst)
            printf("RST ");
        if (tcp_header->psh)
            printf("PSH ");
        if (tcp_header->ack)
            printf("ACK ");
        if (tcp_header->urg)
            printf("URG ");
        printf("\n");
        printf("  Window Size: %u\n", ntohs(tcp_header->window));
        printf("  Checksum: 0x%x\n", ntohs(tcp_header->check));
        printf("  Urgent Ptr: %u\n", ntohs(tcp_header->urg_ptr));

    } // End while

    close(sock);
    return 0;
}

// Create a raw socket to listen for TCP packets
int Open_Raw_Socket(void)
{
    int sock;
    if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0)
    {
        perror("raw socket create error");
        return -1;
    }
    return sock;
}

// Set network interface to promiscuous mode
int Set_Promisc(const char *interface, int sock)
{
    struct ifreq ifr;

    strncpy(ifr.ifr_name, interface, IFNAMSIZ - 1);
    ifr.ifr_name[IFNAMSIZ - 1] = '\0';

    if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
    {
        perror("ioctl SIOCGIFFLAGS error");
        return -1;
    }

    if (ifr.ifr_flags & IFF_PROMISC)
    {
        printf("Interface %s is already in promiscuous mode.\n", interface);
        return 0;
    }

    ifr.ifr_flags |= IFF_PROMISC;

    if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
    {
        perror("ioctl SIOCSIFFLAGS error (maybe run as root?)");
        return -1;
    }

    return 0;
}